
goog.provide('jsmeta.parser.recursive');

function interpret_recursiveRuleCall(ctx) {
  var memo = ctx.memo, item, callKey, seed;
  switch (this.phase) {
    case 0:
      callKey = this.ruleName + ctx.offset;
      if ((item = memo[ callKey ]) && item.inside) {
        if (item.seeds.length > 0) {
          seed = item.seeds.pop();
          this.pattern = seed;
          seed.caller = this;
          if(Parser.globalDebug)Parser.debugBoldLine('000 using a seed for ' + callKey + ' which grew successfully to ' + seed.end);
          this.end = ctx.offset = seed.end;
          this.done = true;
          //if (!(this.done = seed.done))
          //  this.phase = 5;
          item.lastSeed = seed;
          this.outcome = true;
          this.seeds = [];
          this.returns = {};
          this.offsets = {};
          return this.caller;
        } else if (!item.leftRecursed) {
          if(Parser.globalDebug)Parser.debugBoldLine('initial discovery of left recursion with ' + item.id);
          ctx.offset = item.start;
          item.phase = 3;
          item.pattern = ctx.grammar.rules[this.ruleName].clone();
          item.pattern.caller = item;
          item.seeds = [];
          item.returns = {};
          item.offsets = {};
          item.leftRecursed = true;
          return item.pattern;
        } else {
          //if(Parser.globalDebug)Parser.debugBoldLine('unknown failure state 1');
          return this.caller;
        }
      } else {
        memo[ callKey ] = this;
      }
      this.start = ctx.offset;
      this.phase = 1;
      this.pattern = ctx.grammar.rules[this.ruleName].clone();
      this.pattern.caller = this;
      this.inside = true;
      this.seeds = [];
      this.returns = {};
      this.offsets = {};
      return this.pattern;
    case 1:
      if (this.outcome = this.pattern.outcome) {
        callKey = this.ruleName + this.start;
        if (item = memo[ callKey ]) {
          if (item.returns[ctx.offset]) {
            if(Parser.globalDebug)Parser.debugBoldLine('child pattern succeeded, but we already returned from this offset');
            if (!this.pattern.done) {
              if(Parser.globalDebug)Parser.debugBoldLine('so retrying child pattern');
              ctx.offset = this.start;
              return this.pattern;
            }
            if(Parser.globalDebug)Parser.debugBoldLine('so failing');
            this.outcome = false;
            return this.caller;
          } else {
            item.returns[ctx.offset] = true;
          }
        }
        this.end = this.pattern.end = ctx.offset;
        if (!(this.done = this.pattern.done))
          this.phase = 2;
      } else {
        callKey = this.ruleName + this.start;
        if ((item = memo[ callKey ]) && item.seeds.length > 0) {
          seed = item.seeds.pop();
          if (!item.returns[ seed.end ]) {
            this.pattern = seed;
            seed.caller = this;
            if(Parser.globalDebug)Parser.debugBoldLine('111 using a seed for ' + callKey + ' which grew successfully to ' + seed.end);
            this.phase = 2;
            this.outcome = true;
            item.returns[ this.end = ctx.offset = seed.end ] = true;
            this.inside = false;
            return this.caller;
          }
          if (!seed.done) {
            if(Parser.globalDebug)Parser.debugBoldLine('trying seed again');
            this.pattern = seed;
            seed.caller = this;
            this.inside = true;
            return seed;
          }
          if(Parser.globalDebug)Parser.debugBoldLine('seed was done');
        }
        this.end = this.pattern.end = ctx.offset;
        if (!(this.done = this.pattern.done))
          this.phase = 2;
      }
      if(Parser.globalDebug)Parser.debugBoldLine('Child pattern ' + (this.pattern.outcome ? 'succeeded' : 'failed'));
      this.inside = false;
      return this.caller;
    case 2:
      if(Parser.globalDebug)Parser.debugBoldLine('backtracking to child pattern');
      this.phase = 1;
      this.inside = true;
      return this.pattern;
    case 3:
      callKey = this.ruleName + this.start;
      if (this.pattern.outcome && !this.returns[ctx.offset] && !this.offsets[ctx.offset]) {
        if(Parser.globalDebug)Parser.debugBoldLine('the pattern ' + callKey + ' grew successfully to ' + ctx.offset);
        this.pattern.end = ctx.offset;
        this.seeds.push(this.pattern);
        this.offsets[ ctx.offset ] = true;
        this.pattern = this.pattern.clone();
        this.pattern.caller = this;
        ctx.offset = this.start;
        return this.pattern;
      }
      if (this.pattern.outcome && !this.pattern.done) {
        ctx.offset = this.start;
        return this.pattern;
      }
      if (this.seeds.length > 0) {
        seed = this.seeds.pop();
        if(Parser.globalDebug)Parser.debugBoldLine('333 using a seed for ' + callKey + ' which grew successfully to ' + seed.end);
        this.outcome = true;
        this.pattern = seed;
        seed.caller = this;
        if (!(this.done = seed.done))
          this.phase = 4;
        this.end = ctx.offset = seed.end;
        this.returns[ this.end ] = true;
      } else if (this.lastSeed && this.lastSeed.outcome) {
        seed = this.lastSeed;
        if(Parser.globalDebug)Parser.debugBoldLine('333 using the last seed for ' + callKey + ' which grew successfully to ' + seed.end);
        this.outcome = true;
        this.pattern = seed;
        seed.caller = this;
        if (!(this.done = seed.done))
          this.phase = 4;
        this.end = ctx.offset = seed.end;
        this.returns[ this.end ] = true;
      } else {
        if(Parser.globalDebug)Parser.debugBoldLine('no seed for ' + callKey);
        this.outcome = false;
      }
      this.inside = false;
      return this.caller;
    case 4:
      if(Parser.globalDebug)Parser.debugBoldLine('backtracking to child pattern');
      this.phase = 3;
      this.inside = true;
      return this.pattern;
    case 5:
      if(Parser.globalDebug)Parser.debugBoldLine('backtracking to child pattern');
      this.phase = 6;
      this.inside = true;
      return this.pattern;
    case 6:
      if (!(this.done = this.pattern.done))
        this.phase = 5;
      if (this.outcome = this.pattern.outcome)
        this.end = ctx.offset;
      return this.caller;
  }
}
